home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 2
/
Aminet AMIGA CDROM (1994)(Walnut Creek)[Feb 1994][W.O. 44790-1].iso
/
Aminet
/
util
/
gnu
/
oleo_src.lha
/
src
/
cell.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-08-18
|
29KB
|
1,287 lines
/* Copyright (C) 1990 Free Software Foundation, Inc.
This file is part of Oleo, the GNU Spreadsheet.
Oleo is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
Oleo is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Oleo; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "funcdef.h"
#include <stdio.h>
#define obstack_chunk_alloc ck_malloc
#define obstack_chunk_free free
#include "obstack.h"
#include "sysdef.h"
#include "global.h"
#include "cell.h"
/* How many rows/columns to allocate to either side of an allocated cell */
#ifdef __TURBOC__
#define ROW_BUFF 0
#define COL_BUFF 0
#else
#define ROW_BUFF 4
#define COL_BUFF 2
#endif
#ifdef REVERSE
#define MIN_R MIN_COL
#define MAX_R MAX_COL
#define MIN_C MIN_ROW
#define MAX_C MAX_ROW
#else
#define MIN_R MIN_ROW
#define MAX_R MAX_ROW
#define MIN_C MIN_COL
#define MAX_C MAX_COL
#endif
extern void byte_free EXT1(unsigned char *);
extern void flush_variables EXT0();
typedef struct col COL;
typedef struct row ROW;
/* Cells are stored in a two-dimentional sparse array. This allows us to have
a fair-sized namespace (255x255 or 65535x65535) without using incredible
amounts of memory. (64Kx64K cells would use up 120GIG of memory just for
the cell structures. . .)
Note that the sparse arrays are not currently garbage collected, although
flush_everything will free all the row and column structures, as well
as everything in them. . .
*/
struct row {
ROW *row_next;
CELLREF row_low;
CELLREF row_high;
COL *cols[1];
};
static ROW *the_rows;
struct col {
struct col *col_next;
CELLREF col_low;
CELLREF col_high;
CELL cells[1];
};
static ROW *make_row_at EXT3(CELLREF, ROW *, ROW **);
static ROW *row_alloc EXT3N(CELLREF, CELLREF, ROW *);
static void out_of_date EXT0();
static COL *col_alloc EXT3N(CELLREF, CELLREF, COL *);
extern CELL *my_cell;
extern CELLREF cur_row,cur_col;
#define row_free(x) out_of_date(),free(x)
#define col_free(x) \
do{\
out_of_date();\
if( my_cell \
&& my_cell>= &(x->cells[0]) \
&& my_cell<=&(x->cells[x->col_high-x->col_low])) \
my_cell=find_cell(cur_row,cur_col); \
free(x); \
} while (0)
#define MIN(x,y) ((x)<(y) ? (x) : (y))
#define MAX(x,y) ((x)>=(y) ? (x) : (y))
/* find_cell() returns a pointer to a cell at 'row', 'col'. If there is no
cell currently allocated there, it returns 0. */
CELL *
find_cell FUN2(CELLREF, row, CELLREF, col)
{
ROW *row_ptr;
COL *col_ptr;
#ifdef REVERSE
CELLREF tmp;
tmp=row;
row=col;
col=tmp;
#endif
#ifdef TEST
if(row<MIN_R || row>MAX_R || col<MIN_C || col>MAX_C)
panic("%u,%u out of range in find_cell",row,col);
#endif
for(row_ptr=the_rows;row_ptr;row_ptr=row_ptr->row_next) {
if(row_ptr->row_low<=row && row_ptr->row_high>=row)
break;
if(row_ptr->row_low>row) {
row_ptr=0;
break;
}
}
if(!row_ptr)
return 0;
for(col_ptr=row_ptr->cols[row-row_ptr->row_low];col_ptr;col_ptr=col_ptr->col_next) {
if(col_ptr->col_low<=col && col_ptr->col_high>=col)
break;
if(col_ptr->col_low>col) {
col_ptr=0;
break;
}
}
if(!col_ptr)
return 0;
return &(col_ptr->cells[col-col_ptr->col_low]);
}
/* find_or_make_cell() is like find_cell(), except that if there is no cell
allocated there, it allocates one. It should never return zero. */
CELL *
find_or_make_cell FUN2(CELLREF, row, CELLREF, col)
{
ROW *row_ptr;
COL *col_ptr;
COL **col_prev;
#ifdef REVERSE
CELLREF tmp;
tmp=row;
row=col;
col=tmp;
#endif
#ifdef TEST
if(row<MIN_R || row>MAX_R || col<MIN_C || col>MAX_C)
panic("%u,%u out of range in find_or_make_cell",row,col);
#endif
row_ptr=make_row_at(row,the_rows,&the_rows);
/* cdr down the list of cols until we find the one we want */
col_prev= &(row_ptr->cols[row-row_ptr->row_low]);
for(col_ptr= *col_prev;col_ptr;col_ptr= *col_prev) {
/* Have we found the right place? */
if(col_ptr->col_low<=col && col_ptr->col_high>=col)
break;
if(col_ptr->col_low>col) {
/* We've cdr'd past the place in the list where
the column goes */
if(col_ptr->col_low>col+COL_BUFF+1) {
/* There isn't anything nearby. */
col_ptr=0;
} else {
int new_low;
int old_num_cols;
COL *new_col;
/* Grow the column structure down a bit so
that this column will fit in it */
new_low=col-COL_BUFF;
if(new_low<MIN_C)
new_low=MIN_C;
old_num_cols=1+col_ptr->col_high-col_ptr->col_low;
new_col=col_alloc(new_low,
col_ptr->col_high,
col_ptr->col_next,
old_num_cols,
col_ptr->col_low,
&(col_ptr->cells[0]),
0);
*col_prev=new_col;
col_free(col_ptr);
col_ptr=new_col;
}
break;
}
if(col_ptr->col_high+COL_BUFF+1>=col) {
/* The column doesn't fit in the current
col structure, but it can be made to by stretching
or by merging two col structures.
So do it. */
if(col_ptr->col_next && col_ptr->col_next->col_low<=col+COL_BUFF+1) {
/* This col structure is close enough to the
next one that they should just be merged */
COL *the_next;
COL *new_col;
the_next=col_ptr->col_next;
if(the_next->col_low<=col) {
col_ptr=the_next;
break;
}
new_col=col_alloc(col_ptr->col_low,
the_next->col_high,
the_next->col_next,
1+col_ptr->col_high-col_ptr->col_low,
col_ptr->col_low,
&(col_ptr->cells[0]),
1+the_next->col_high-the_next->col_low,
the_next->col_low,
&(the_next->cells[0]),
0);
*col_prev=new_col;
col_free(col_ptr);
col_free(the_next);
col_ptr=new_col;
} else {
int new_high;
COL *new_col;
/* Stretch the col structure a little */
if(col>MAX_C-COL_BUFF)
new_high=MAX_C;
else
new_high=col+COL_BUFF;
new_col=col_alloc(col_ptr->col_low,
new_high,
col_ptr->col_next,
1+col_ptr->col_high-col_ptr->col_low,
col_ptr->col_low,
&(col_ptr->cells[0]),
0);
*col_prev=new_col;
col_free(col_ptr);
col_ptr=new_col;
}
break;
}
col_prev= &(col_ptr->col_next);
}
if(!col_ptr) {
/* We couldn't find any columns. Create some */
if(col<MIN_C+COL_BUFF)
col_ptr=col_alloc(MIN_C,
col+COL_BUFF,
*col_prev,
0);
else if(col>MAX_C-COL_BUFF)
col_ptr=col_alloc(col-COL_BUFF,
MAX_C,
*col_prev,
0);
else
col_ptr=col_alloc(col-COL_BUFF,
col+COL_BUFF,
*col_prev,
0);
*col_prev= col_ptr;
}
return &(col_ptr->cells[col-col_ptr->col_low]);
}
/* These two return the maximum row/column allocated at the given column/row
Because of the way the sparse array works, one of them will return the
same value for all possible inputs.
*/
CELLREF
#ifdef REVERSE
max_col FUN1(CELLREF, row)
#else
max_row FUN1(CELLREF, col)
#endif
{
ROW *rr;
if(!the_rows)
return MIN_R;
for(rr=the_rows;rr->row_next;rr=rr->row_next)
;
return rr->row_high;
}
CELLREF
#ifdef REVERSE
max_row FUN1(CELLREF, row)
#else
max_col FUN1(CELLREF, row)
#endif
{
ROW *rr;
COL *cc;
if(!the_rows)
return MIN_C;
for(rr=the_rows;rr;rr=rr->row_next)
if(rr->row_low<=row && rr->row_high>=row)
break;
if(!rr)
return MIN_C;
for(cc=rr->cols[row-rr->row_low];cc && cc->col_next;cc=cc->col_next)
;
if(!cc)
return MIN_C;
return cc->col_high;
}
/* These two return the highest row/column allocated anywhere in the
spreadsheet. One of them is easy to find, but the other requires scanning
the entire sparse array
*/
CELLREF
#ifdef REVERSE
highest_col FUN0()
#else
highest_row FUN0()
#endif
{
ROW *rr;
if(!the_rows)
return MIN_R;
for(rr=the_rows;rr->row_next;rr=rr->row_next)
;
return rr->row_high;
}
CELLREF
#ifdef REVERSE
highest_row FUN0()
#else
highest_col FUN0()
#endif
{
CELLREF ret;
int n;
ROW *rr;
COL *cc;
if(!the_rows)
return MIN_C;
ret=MIN_C;
for(rr=the_rows;rr;rr=rr->row_next) {
for(n=0;n<rr->row_high-rr->row_low;n++) {
for(cc=rr->cols[n];cc && cc->col_next;cc=cc->col_next)
;
if(cc && cc->col_high>ret)
ret=cc->col_high;
}
}
return ret;
}
/* This is a complicated function. It takes four or more args.
The first three are
The lowest row value we should create
The highest row value we should create
the next row structure in the list
and groups of
how many col ptrs are in this group (N_IN)
what row value this group starts at (ROW_IN)
and a pointer to an array of struct col *s (COL_IN)
The groups end with a (partial) group whose N_IN is zero
*/
static ROW *
row_alloc FUN3N(CELLREF,r_low,CELLREF,r_high,ROW *,r_next)
{
ROW *ret;
int num_rows;
int our_row;
COL **colp;
int n_in;
int row_in;
COL **col_in;
va_list args;
#ifdef TEST
if(r_next && r_next->row_low==r_high+1)
error_msg("Warning: Consecutive rows allocated");
#endif
out_of_date();
num_rows=1+r_high-r_low;
ret=(ROW *)ck_malloc(sizeof(ROW)+(num_rows-1)*sizeof(ROW *));
ret->row_low=r_low;
ret->row_high=r_high;
ret->row_next=r_next;
/* printf("row_alloc(%d,%d,%d,",r_low,r_high,r_next); */
var_start(args,r_next);
our_row=r_low;
colp= &(ret->cols[0]);
while(n_in=va_arg(args,int)) {
row_in=va_arg(args,/* CELLREF */ int);
while(our_row<row_in) {
our_row++;
*colp++=0;
}
col_in=(COL **)va_arg(args,COL **);
/* printf(" %d,%d,%d,",n_in,row_in,col_in); */
while(n_in--) {
our_row++;
*colp++ = *col_in++;
}
}
while(our_row++<=r_high)
*colp++=0;
/* printf("0)\n"); */
va_end(args);
return ret;
}
/* This is like row_alloc(), but it allocates columns instead */
static COL *
col_alloc FUN3N(CELLREF,c_low,CELLREF,c_high,COL *,c_next)
{
COL *ret;
int num_cols;
int our_col;
CELL *cellp;
int n_in;
int col_in;
CELL *cell_in;
va_list args;
#ifdef TEST
if(c_next && c_next->col_low==c_high+1)
error_msg("Warning: Consecutive cols allocated");
#endif
out_of_date();
num_cols=1+c_high-c_low;
ret=(COL *)ck_malloc(sizeof(COL)+(num_cols-1)*sizeof(CELL));
ret->col_low=c_low;
ret->col_high=c_high;
ret->col_next=c_next;
/* printf("col_alloc(%d,%d,%d,",c_low,c_high,c_next); */
var_start(args,c_next);
our_col=c_low;
cellp= &(ret->cells[0]);
while(n_in=va_arg(args,int)) {
col_in=va_arg(args,/* CELLREF */ int);
while(our_col<col_in) {
our_col++;
/* cellp->cell_string=0; */
cellp->cell_refs_from=0;
cellp->cell_refs_to=0;
cellp->cell_cycle=0;
cellp->cell_formula=0;
cellp++->cell_flags=0;
}
cell_in=(CELL *)va_arg(args,CELL *);
/* printf(" %d,%d,%d,",n_in,col_in,cell_in); */
bcopy(cell_in,cellp,n_in*sizeof(CELL));
our_col+=n_in;
cellp+=n_in;
}
/* printf(" 0)\n"); */
while(our_col++<=c_high) {
/* cellp->cell_string=0; */
cellp->cell_refs_from=0;
cellp->cell_refs_to=0;
cellp->cell_cycle=0;
cellp->cell_formula=0;
cellp++->cell_flags=0;
}
return ret;
}
/* This function returns a pointer to a row structure that has a row defined
at ROW. PREVP points to a row structure somewhere before ROW. PREVPP
points to the place where the pointer to PREVP is stored (for use if
we have to realloc(PREVP,...) */
static ROW *
make_row_at FUN3(CELLREF,row,ROW *,prevp,ROW **,prevpp)
{
ROW *postp;
ROW *new_row;
CELLREF rowhi,rowlo;
while(prevp && prevp->row_next && prevp->row_next->row_low < row) {
prevpp = &(prevp->row_next);
prevp=prevp->row_next;
}
if(prevp && prevp->row_low<=row && prevp->row_high>=row)
return prevp;
rowhi= (row>MAX_R-ROW_BUFF) ? MAX_R : row+ROW_BUFF;
rowlo= (row<MIN_R+ROW_BUFF) ? MIN_R : row-ROW_BUFF;
if( !prevp
|| ( prevp->row_high < rowlo-1
&& (!prevp->row_next || prevp->row_next->row_low-1>rowhi))) {
/* There's no row structure there. Therefore, create one */
new_row=row_alloc(rowlo,
rowhi,
prevp ? prevp->row_next : *prevpp,
0);
if(prevp)
prevp->row_next=new_row;
else
*prevpp=new_row;
return new_row;
}
if(prevp->row_high+1>=rowlo) {
if(prevp->row_next && prevp->row_next->row_low-1<=rowhi) {
/* This row is (nearly) midway between two row
structures. Merge them */
postp=prevp->row_next;
new_row=row_alloc(prevp->row_low,
postp->row_high,
postp->row_next,
1+prevp->row_high-prevp->row_low,
prevp->row_low,
&(prevp->cols[0]),
1+postp->row_high-postp->row_low,
postp->row_low,
&(postp->cols[0]),
0);
*prevpp=new_row;
row_free(prevp);
row_free(postp);
return new_row;
} else if(prevp->row_low>rowlo) {
/* The first row struct must be stretched up a bit */
new_row=row_alloc(rowlo,prevp->row_high,prevp->row_next,
1+prevp->row_high-prevp->row_low,
prevp->row_low,
&(prevp->cols[0]),
0);
*prevpp=new_row;
row_free(prevp);
return new_row;
} else {
/* This row is just past the end of a row
structure. Stretch the structure a bit so
that it'll fit */
new_row=row_alloc(prevp->row_low,
rowhi,
prevp->row_next,
1+prevp->row_high-prevp->row_low,
prevp->row_low,
&(prevp->cols[0]),
0);
*prevpp=new_row;
row_free(prevp);
return new_row;
}
}
if(prevp->row_next && prevp->row_next->row_low-1<=rowhi) {
/* Grow the old row_structure down just a
little so that this one will fit in it */
prevpp= &(prevp->row_next);
postp=prevp->row_next;
new_row=row_alloc(rowlo,
postp->row_high,
postp->row_next,
1+postp->row_high-postp->row_low,
postp->row_low,
&(postp->cols[0]),
0);
*prevpp=new_row;
row_free(postp);
return new_row;
}
#ifdef TEST
if(prevp->row_low<row || prevp->row_high>row)
panic("Make row at: %u not in %u %u",row,prevp->row_low,prevp->row_high);
#endif
return prevp;
}
/* The next group of routines deal with finding groups of cells in the
spreadsheet. They should be smart enough to deal with the spreadsheet
changing under them. */
struct find {
CELLREF lr,lc,hr,hc;
CELLREF cr,cc;
int flags;
ROW *nowrow;
COL **colpptr;
COL *nowcol;
int colsleft;
CELL *nowcell;
int cellsleft;
struct find *next;
};
static struct find *f;
static struct obstack find_stack;
void
init_cells FUN0()
{
obstack_begin(&find_stack,sizeof(struct find)*15);
}
/* This sets things up so that the next gzillion calls to next_cell_in_range()
will return one of the cells in the region delineated by RNG. If
This will skip over any gaps in the region, but will return zeroed cells
in the region. If there are no cells in the region, the first call to
next_cell_in_range() will return 0
*/
void
find_cells_in_range FUN1(struct rng *,rng)
{
struct find *oldf;
#ifdef REVERSE
#define LR rng->lc
#define LC rng->lr
#define HR rng->hc
#define HC rng->hr
#else
#define LR rng->lr
#define LC rng->lc
#define HR rng->hr
#define HC rng->hc
#endif
#ifdef TEST
if( LR<MIN_R || LR>MAX_R || HR<MIN_R || HR>MAX_R
|| LC<MIN_C || LC>MAX_C || HC<MIN_C || HC>MAX_C)
panic("find_cells_in_range(%u:%u %u:%u) out of range",LR,HR,LC,HC);
#endif
oldf=f;
f=obstack_alloc(&find_stack,sizeof(struct find));
bzero(f,sizeof(struct find));
f->next=oldf;
f->lr=LR;
f->hr=HR;
f->lc=LC;
f->hc=HC;
for(f->nowrow=the_rows;f->nowrow;f->nowrow=f->nowrow->row_next)
if(f->nowrow->row_high>=f->lr)
break;
while(f->nowrow && f->nowrow->row_low<=f->hr) {
f->cr=f->lr<=f->nowrow->row_low ? f->nowrow->row_low : f->lr;
f->colpptr= &(f->nowrow->cols[f->cr-f->nowrow->row_low]);
f->colsleft=1+MIN(f->hr,f->nowrow->row_high)-f->cr;
while(f->colsleft) {
--(f->colsleft);
f->nowcol= *(f->colpptr)++;
while(f->nowcol && f->nowcol->col_high<f->lc)
f->nowcol=f->nowcol->col_next;
if(f->nowcol && f->nowcol->col_low<=f->hc) {
/* Found something */
f->cc=(f->lc<f->nowcol->col_low) ? f->nowcol->col_low : f->lc;
f->nowcell= &(f->nowcol->cells[f->cc-f->nowcol->col_low]);
f->cellsleft=1+MIN(f->hc,f->nowcol->col_high)-f->cc;
--(f->cc);
#ifdef TEST
if(f->cellsleft<1)
panic("Cellsleft %d<1",f->cellsleft);
#endif
f->flags=0;
return;
}
(f->cr)++;
}
f->nowrow=f->nowrow->row_next;
}
/* If we get here, we searched through the entire range, and didn't
find *any* cells. We lose */
f->flags=1;
}
/* This is like find_cells_in_range, except that if the cells don't exist,
it makes them! */
void
make_cells_in_range FUN1(struct rng *,rng)
{
ROW **prevpp;
ROW *myrow;
int n;
COL **colpp;
COL *mycol;
struct find *oldf;
CELLREF lr,hr,lc,hc;
#ifdef TEST
if( LR<MIN_R || LR>MAX_R || HR<MIN_R || HR>MAX_R
|| LC<MIN_C || LC>MAX_C || HC<MIN_C || HC>MAX_C)
panic("make_cells_in_range(%u:%u %u:%u) out of range",LR,HR,LC,HC);
#endif
oldf=f;
f=obstack_alloc(&find_stack,sizeof(struct find));
bzero(f,sizeof(struct find));
f->next=oldf;
f->lr=LR;
f->lc=LC;
f->hr=HR;
f->hc=HC;
f->colsleft=f->hr-f->lr;
f->cr=f->lr;
f->cellsleft=1+f->hc-f->lc;
f->cc=f->lc-1;
lr= f->lr<=MIN_R+ROW_BUFF ? MIN_R : f->lr-ROW_BUFF;
hr= f->hr>=MAX_R-ROW_BUFF ? MAX_R : f->hr+ROW_BUFF;
if(!the_rows) {
myrow=the_rows=row_alloc(lr,hr,0,0);
} else {
myrow=the_rows;
prevpp= &the_rows;
while(myrow && myrow->row_high<lr-1) {
prevpp= &(myrow->row_next);
myrow=myrow->row_next;
}
if(!myrow || myrow->row_low-1>hr)
myrow= *prevpp=row_alloc(lr,hr,myrow,0);
else if(myrow->row_high<hr) {
ROW *t1,*t2;
while(myrow->row_high<hr && myrow->row_next && myrow->row_next->row_low<=hr+1) {
t1=myrow;
t2=myrow->row_next;
myrow= *prevpp=row_alloc(t1->row_low,t2->row_high,t2->row_next,
1+t1->row_high-t1->row_low,t1->row_low,&(t1->cols[0]),
1+t2->row_high-t2->row_low,t2->row_low,&(t2->cols[0]),
0);
row_free(t1);
row_free(t2);
}
if(myrow->row_high<hr) {
t1=myrow;
myrow= *prevpp=row_alloc(t1->row_low,hr,t1->row_next,
1+t1->row_high-t1->row_low,t1->row_low,&(t1->cols[0]),
0);
row_free(t1);
}
} else if(myrow->row_low>lr) {
ROW *t1;
t1=myrow;
myrow= *prevpp=row_alloc(lr,t1->row_high,t1->row_next,
1+t1->row_high-t1->row_low,t1->row_low,&(t1->cols[0]),
0);
row_free(t1);
}
}
#ifdef TEST
if(myrow->row_low>f->lr || myrow->row_high<f->hr)
panic("Myrow isn't big enough");
#endif
lc= f->lc<=MIN_C+COL_BUFF ? MIN_C : f->lc-COL_BUFF;
hc= f->hc>=MAX_C-COL_BUFF ? MAX_C : f->hc+COL_BUFF;
for(n=lr-myrow->row_low;n<=hr-myrow->row_low;n++) {
colpp= &(myrow->cols[n]);
if(!*colpp) {
*colpp=col_alloc(lc,hc,0,0);
} else {
for(mycol= *colpp;mycol;colpp= &(mycol->col_next),mycol= *colpp)
if(mycol->col_high+1>=lc)
break;
if(!mycol || mycol->col_low>hc+1)
*colpp=col_alloc(lc,hc,mycol,0);
else if(mycol->col_low<=lc && mycol->col_high>=hc)
; /* Easy */
else /* if(mycol->col_low>lc && mycol->col_high>hc) {
COL *t1;
t1=mycol;
mycol= *colpp=col_alloc(lc,t1->col_high,t1->col_next,
1+t1->col_high-t1->col_low,t1->col_low,&(t1->cells[0]),
0);
col_free(t1);
} else */ {
COL *t1,*t2;
while(mycol->col_high<=hc+1
&& mycol->col_next
&& mycol->col_next->col_low<=hc+1) {
t1=mycol;
t2=mycol->col_next;
mycol= *colpp=col_alloc(MIN(lc,t1->col_low),t2->col_high,t2->col_next,
1+t1->col_high-t1->col_low,t1->col_low,&(t1->cells[0]),
1+t2->col_high-t2->col_low,t2->col_low,&(t2->cells[0]),
0);
col_free(t1);
col_free(t2);
}
if(mycol->col_low>lc || mycol->col_high<hc) {
t1=mycol;
mycol= *colpp=col_alloc(MIN(lc,t1->col_low),MAX(hc,mycol->col_high),t1->col_next,
1+t1->col_high-t1->col_low,t1->col_low,&(t1->cells[0]),
0);
col_free(t1);
}
}
}
}
f->nowrow=myrow;
f->colpptr= &(f->nowrow->cols[f->lr-f->nowrow->row_low]);
f->nowcol= *(f->colpptr)++;
while(f->nowcol->col_high<f->lc)
f->nowcol=f->nowcol->col_next;
f->nowcell= &(f->nowcol->cells[f->lc-f->nowcol->col_low]);
#ifdef TEST
if(f->cellsleft<1)
panic("Cellsleft %d<1",f->cellsleft);
#endif
f->flags=0;
}
/* Return the next cell in the range previously selected by
find_cells_in_range() or make_cells_in_range() If row_alloc(),
row_free(), col_alloc(), or col_free() has been called, re-sync to the
current state of the spreadsheet. */
CELL *
next_cell_in_range FUN0()
{
struct find *oldf;
#ifdef TEST
if(!f)
panic("No 'f' in next_cells_in_range!");
#endif
if(f->flags) {
if(f->flags&1) {
done:
oldf=f->next;
(void)obstack_free(&find_stack,f);
f=oldf;
return 0;
}
f->flags=0;
if(f->cr==f->hr && f->cc==f->hc)
goto done;
if(f->cc==f->hc) { /* JF was f->cc=f->hc */
f->cc=f->lc-1;
(f->cr)++;
}
for(f->nowrow=the_rows;f->nowrow;f->nowrow=f->nowrow->row_next)
if(f->nowrow->row_high>=f->cr)
break;
if(!f->nowrow)
goto done;
f->colpptr= &(f->nowrow->cols[f->cr-f->nowrow->row_low]);
f->colsleft= 1+MIN(f->hr,f->nowrow->row_high)-f->cr;
if(f->colsleft) {
--(f->colsleft);
f->nowcol= *(f->colpptr)++;
while(f->nowcol && f->nowcol->col_high<f->cc)
f->nowcol=f->nowcol->col_next;
if(f->nowcol && f->nowcol->col_low<=f->cc) {
(f->cc)++;
f->nowcell= &(f->nowcol->cells[f->cc-f->nowcol->col_low]);
f->cellsleft=1+MIN(f->hc,f->nowcol->col_high)-f->cc;
--(f->cc);
}
}
}
if(f->cellsleft) {
#ifdef TEST
if(f->cellsleft<1)
panic("Cellsleft %d<1",f->cellsleft);
#endif
--(f->cellsleft);
(f->cc)++;
return (f->nowcell)++;
}
/* There are no more cells in the current COL structure. If there
might be cells in the next one, try there */
if(f->nowcol->col_high<f->hc) {
f->nowcol=f->nowcol->col_next;
if(f->nowcol && f->nowcol->col_low<=f->hc) {
f->nowcell= &(f->nowcol->cells[0]);
f->cellsleft=MIN(f->hc,f->nowcol->col_high)-f->nowcol->col_low;
f->cc=f->nowcol->col_low;
#ifdef TEST
if(f->cellsleft<0)
panic("Cellsleft %d<1",f->cellsleft);
#endif
return (f->nowcell)++;
}
}
try_cols:
/* See if there are any more COLS in the current row structure
If there are, scan through them for useful cells. */
while(f->colsleft) {
--(f->colsleft);
(f->cr)++;
f->nowcol= *(f->colpptr)++;
while(f->nowcol && f->nowcol->col_high<f->lc)
f->nowcol=f->nowcol->col_next;
if(f->nowcol && f->nowcol->col_low<=f->hc) { /* Found something */
f->cc=f->lc<f->nowcol->col_low ? f->nowcol->col_low : f->lc;
f->nowcell= &(f->nowcol->cells[f->cc-f->nowcol->col_low]);
f->cellsleft=MIN(f->hc,f->nowcol->col_high)-f->cc;
#ifdef TEST
if(f->cellsleft<0)
panic("Cellsleft %d<1",f->cellsleft);
#endif
return (f->nowcell)++;
}
}
/* Have we run out of rows? */
if(f->nowrow->row_high>=f->hr)
goto done;
/* Check in the next ROW structure. There's nothing in this one */
f->nowrow=f->nowrow->row_next;
if(!f->nowrow || f->nowrow->row_low>f->hr)
goto done;
f->colpptr= &(f->nowrow->cols[0]);
f->colsleft= 1 + MIN(f->hr,f->nowrow->row_high)-f->nowrow->row_low;
f->cr=f->nowrow->row_low-1;
goto try_cols;
}
/* This is a wrapper to next_cell_in_range that also returns (through
PUTROW and PUTCOL information about where the cell actually *is*
*/
CELL *
next_row_col_in_range FUN2(CELLREF *,putrow, CELLREF *,putcol)
{
CELL *nxt;
nxt=next_cell_in_range();
if(nxt) {
#ifdef REVERSE
*putcol=f->cr;
*putrow=f->cc;
#else
*putrow=f->cr;
*putcol=f->cc;
#endif
} else {
*putrow=MIN_R-1;
*putcol=MIN_C-1;
}
return nxt;
}
/* This should be called if a function called {find,make}_cells_in_range()
and wants to stop calling next_{cell,row_col}_in_range() before they
encounter the end of the range */
void
no_more_cells FUN0()
{
struct find *oldf;
oldf=f->next;
(void)obstack_free(&find_stack,f);
f=oldf;
}
/* This is called by row_alloc and col_alloc to tell
next_{cell,row_col}_in_range() that some part of the spreadsheet has
changed.
*/
static void
out_of_date FUN0()
{
struct find *tmpf;
for(tmpf=f;tmpf;tmpf=tmpf->next)
tmpf->flags|=2;
}
/* this is your basic trash-the-world function. */
void
flush_everything FUN0()
{
ROW *rp,*rnxt;
COL *cp,*cnxt;
CELL *sp;
int nrn;
#ifndef SPLIT_REFS
extern void flush_refs();
#endif
for(rp= the_rows;rp;rp=rnxt) {
rnxt=rp->row_next;
for(nrn=rp->row_low;nrn<=rp->row_high;nrn++) {
for(cp= rp->cols[nrn-rp->row_low];cp;cp= cnxt) {
cnxt=cp->col_next;
for(sp= &(cp->cells[0]);sp<=&(cp->cells[cp->col_high-cp->col_low]);sp++) {
#ifdef SPLIT_REFS
if(sp->cell_refs_from)
free(sp->cell_refs_from);
if(sp->cell_refs_to)
free(sp->cell_refs_to);
#endif
if(sp->cell_formula)
byte_free(sp->cell_formula);
if(GET_TYP(sp)==TYP_STR)
free(sp->cell_str);
}
free(cp);
}
}
free(rp);
the_rows=0;
}
flush_variables();
#ifndef SPLIT_REFS
flush_refs();
#endif
}
#ifdef TEST
extern char *bname[];
extern char *dbg_print_formula();
extern char *dbg_print_ref_fm();
extern char *dbg_print_ref_to();
extern char print_buf[];
extern char *bname[];
void dbg_print_cell();
/* These debugging functions store useful text about the sparse array
in the buffer pointed to by BUF. If it isn't big enough, you lose. */
void
dbg_print_rows FUN0()
{
ROW *row_ptr;
CELLREF n,maxx;
char *buf;
for(row_ptr=the_rows;row_ptr;row_ptr=row_ptr->row_next) {
text_line("Row %p: next %p, low %u, high %u",
row_ptr,row_ptr->row_next,row_ptr->row_low,
row_ptr->row_high);
maxx=row_ptr->row_high-row_ptr->row_low;
for(buf=print_buf,n=0;n<=maxx;n++) {
(void)sprintf(buf," %p",row_ptr->cols[n]);
if(n%8==7) {
text_line(print_buf);
buf=print_buf;
} else
buf+=strlen(buf);
}
if(n%8)
text_line(print_buf);
}
}
void
dbg_print_cols FUN1(CELLREF, row)
{
ROW *row_ptr;
COL *col_ptr;
CELLREF maxc,nc;
CELL *cp;
char *buf;
for(row_ptr=the_rows;row_ptr;row_ptr=row_ptr->row_next)
if(row_ptr->row_low<=row && row_ptr->row_high>=row)
break;
if(!row_ptr)
return;
for(col_ptr=row_ptr->cols[row-row_ptr->row_low];col_ptr;col_ptr=col_ptr->col_next) {
text_line("Col %p: Next %p, low %u, high %u",
col_ptr,col_ptr->col_next,col_ptr->col_low,
col_ptr->col_high);
maxc=col_ptr->col_high-col_ptr->col_low;
for(cp= &(col_ptr->cells[0]),buf=print_buf,nc=0;nc<=maxc;nc++,cp++) {
switch(GET_TYP(cp)) {
case 0:
(void)strcpy(buf," .");
break;
case TYP_FLT:
(void)sprintf(buf," %8gf",cp->cell_flt);
break;
case TYP_INT:
(void)sprintf(buf," %8ldi",cp->cell_int);
break;
case TYP_ERR:
(void)sprintf(buf," %8.8se",ename[cp->cell_err]);
break;
case TYP_BOL:
(void)sprintf(buf," %8sb",bname[cp->cell_bol]);
break;
case TYP_STR:
(void)sprintf(buf," %8.8ss",cp->cell_str);
break;
default:
(void)sprintf(buf," %8dU",GET_TYP(cp));
break;
}
if(nc%8==7) {
text_line(print_buf);
buf=print_buf;
} else
buf+=strlen(buf);
}
if(nc%8)
text_line(print_buf);
}
}
void
dbg_print_array FUN0()
{
int maxx;
int nr;
int maxc;
int nc;
COL *col_ptr;
CELL *cp;
ROW * row_ptr;
char *buf;
for(row_ptr=the_rows;row_ptr;row_ptr=row_ptr->row_next) {
text_line("Row %p: next %p, low %u, high %u",
row_ptr,row_ptr->row_next,row_ptr->row_low,
row_ptr->row_high);
maxx=row_ptr->row_high-row_ptr->row_low;
for(nr=0,buf=print_buf;nr<=maxx;nr++) {
(void)sprintf(buf," %p",row_ptr->cols[nr]);
if(nr%8==7) {
text_line(print_buf);
buf=print_buf;
} else
buf+=strlen(buf);
}
if(nr%8)
text_line(print_buf);
for(nr=0;nr<=maxx;nr++) {
for(col_ptr=row_ptr->cols[nr];col_ptr;col_ptr=col_ptr->col_next) {
text_line("Col %p: Next %p, low %u, high %u",
col_ptr,col_ptr->col_next,col_ptr->col_low,
col_ptr->col_high);
maxc=col_ptr->col_high-col_ptr->col_low;
for(cp= &(col_ptr->cells[0]),buf=print_buf,nc=0;nc<=maxc;nc++,cp++) {
(void)sprintf(buf," %p",cp);
if(nc%8==7) {
text_line(print_buf);
buf=print_buf;
} else
buf+=strlen(buf);
}
if(nc%8)
text_line(print_buf);
for(cp= &(col_ptr->cells[0]),nc=0;nc<=maxc;nc++,cp++)
dbg_print_cell(cp);
}
}
}
}
void
dbg_print_cell FUN1(CELL *,cp)
{
char *ptr1,*ptr2;
char tmpbuf[30];
switch(GET_TYP(cp)) {
case 0:
ptr1="(null)";
ptr2="";
break;
case TYP_FLT:
sprintf(tmpbuf,"Float: %.16g",cp->cell_flt);
ptr1=tmpbuf;
ptr2="";
break;
case TYP_INT:
sprintf(tmpbuf,"Int: %ld",cp->cell_int);
ptr1=tmpbuf;
ptr2="";
break;
case TYP_ERR:
sprintf(tmpbuf,"Error: %d: ",cp->cell_err);
ptr1=tmpbuf;
ptr2=ename[cp->cell_err];
break;
case TYP_BOL:
sprintf(tmpbuf,"Bool: %d: ",cp->cell_bol);
ptr1=tmpbuf;
ptr2=bname[cp->cell_bol];
break;
case TYP_STR:
sprintf(tmpbuf,"String: %p: ",cp->cell_str);
ptr1=tmpbuf;
ptr2=cp->cell_str;
break;
default:
sprintf(tmpbuf,"Unknown: %d",GET_TYP(cp));
ptr1=tmpbuf;
ptr2="";
break;
}
text_line("Cell %p: flags %#lx, cycle %u, formula %p, value %s%s",
cp,cp->cell_flags,cp->cell_cycle,cp->cell_formula,
ptr1,ptr2);
dbg_print_formula(cp->cell_formula);
dbg_print_ref_fm(cp->cell_refs_from);
dbg_print_ref_to(cp->cell_refs_to);
}
#endif